Por su naturaleza, una gran cantidad de contenido encontrado en la web es dinámico. Se pueden encontrar conjuntos de datos publicados como datos abiertos linkeados (LOD, por sus siglas en inglés (Linked Open Data)) en la web. La nube LOD contiene varios miles de millones de descripciones de recursos en formato RDF (Resource Description Framework), información legible por máquinas, que describen cosas (recursos) del mundo real y relaciones entre ellas. Estos datos forman un enorme grafo dirigido y etiquetado (los recursos son nodos, las relaciones son aristas), a los que los humanos y agentes de software inteligentes pueden acceder. Al mismo tiempo los datos y enlaces entre los datos son enriquecidos constantemente por humanos y máquinas (por ejemplo, convertidores de datos o sistemas de administración de contenido).
Varios autores han abordado la dinámica de la web en general, sin embargo los enfoques existentes de la web tradicional no son suficientes para explorar y descubrir las dinámicas en la Web de Datos, ya que no tienen en cuenta las características especiales de LOD. El entendimiento de la dinámica en LOD, entre otras cosas podría ayudar a decidir qué consultas o partes de esta pueden ejecutarse sobre una memoria caché, y cuales en vivo a través del contenido web para garantizar resultados actualizados a menor costo. Además existen diferentes temas relacionados con la administración de datos web que pueden beneficiarse de una comprensión más profunda de la dinámica de los conjuntos de datos, como son: rastreo y almacenamiento en la web [Cho y Garcia-Molina, 2003], mantenimiento de la integridad de enlaces [Haslhofer y Popitsch, 2009] o el servicio de consultas continuas [Pandey et al., 2003].
En la actualidad es creciente el número de aplicaciones que consultan información de LOD para ofrecer variados servicios. La forma estándar de consultar este tipo de datos es mediante el lenguaje de consultas SPARQL. En este contexto, nos proponemos analizar la dinamicidad de los datos en la web a través del estudio de consultas en lenguaje SPARQL e intentaremos responder las siguientes preguntas:
Con el objetivo de abordar el problema de estimar cambios futuros en los resultados de las consultas y tomando como hipótesis que, la dinamicidad de los resultados de una consulta se puede predecir a partir de la propia consulta y estadísticas de la dinamicidad de los datos, emplearemos 23 versiones del grafo de conocimiento de Wikidata y una colección de aproximadamente 400 consultas extraídas de la página de Wikidata. Seleccionamos Wikidata porque proporciona un historial de versiones semanales que podemos usar para evaluar predicciones y está editado por miles de usuarios colaborativamente, lo que significa que se observan cambios significativos semanalmente.
Dataset RDF
Utilizaremos 23 versiones de Wikidata en el período del 18/04/2017 al 27/09/2017, que se capturaron semanalmente. Trabajamos con la versión de truthy en el formato RDF N-triples, que representa declaraciones que tienen el mejor rango no en desuso para una propiedad determinada. En relación a las versiones se puede decir que:
En la Figura 1 se muestra el crecimiento mantenido durante el período de algunos de estos indicadores. El salto en la semana 12 se debe al hecho de que no fue posible obtener los datos para esa semana. De manera general se puede observar que la información es relativamente incremental, pero existen eliminaciones. La Figura 2 muestra la proporción de triples eliminados y agregados en cada semana.
Dataset de consultas
Una vez que se tiene el conjunto de datos dinámico, con dominios cruzados y con información histórica sobre su evolución, es necesario tener una colección de consultas para el conjunto de datos. Al momento de comenzar con el trabajo, no se encontró un conjunto de datos estructurado con consultas siguiendo el modelo de datos de Wikidata, sino que se encontró un conjunto de 388 consultas de ejemplos sobre Wikidata, distribuidas en 18 categorías, que incluyen: simple, showcase, Wikibase predicates, Wikimedia projects, entertainment, geography, demography, politics, economics, and business, science, history, culture, food & drink, sports, women, bibliographic citation (Wikicite), maintenance, europeana, and unsorted. Estas consultas serán consideradas para evaluar nuestra hipótesis.
Figura 1: A la izquierda, evolución del número de triples en Wikidata. A la derecha: triples añadidos y eliminados en Wikidata.
Las consultas requerían ser procesadas manualmente, antes de realizar un análisis sobre ellas, porque tenían errores de sintaxis; y otras no fueron consideradas, porque solicitaron información externa a Wikidata, como DBpedia y bigdata. Consideramos que los resultados de la consulta han cambiado si no son los mismos resultados que se obtuvieron para esta consulta en la evaluación anterior. Por lo tanto, se necesitan consultas deterministas, es decir, eliminar las que podrían cambiar su resultado incluso cuando los datos son los mismos. En nuestro caso, se eliminaron las consultas con la palabra clave SAMPLE y agregamos ORDER BY a todas las consultas con todas las variables proyectadas. Este último cambio facilita la detección de cambios en los resultados al iterar sobre ambos conjuntos de resultados ordenados. Al finalizar el procesamiento sobre las consultas, se obtiene un total de 221 consultas. Sobre estas se realizó un análisis del uso básico de las características SPARQL contando las palabras clave en las consultas (ver Tabla 1).
Tabla 1: A la izquierda, frecuencias de Keywords en las consultas. En el centro, Patrones triples (C = Constante, V = Variable). A la derecha, la frecuencia de uso de las reglas en los Patrones de triples.
En la primera parte de la Tabla 1 (a la izquierda) se describe el tipo de consultas y se puede apreciar que solo tiene consultas SELECT. La segunda parte contiene modificadores de solución, y se observa que todas (100%) las consultas utilizan ORDER BY, lo cual se debe al proceso de determinación utilizado, LIMIT se usa en 18.10% y DISTINCT en 29.41%. La tercera parte tiene palabras clave asociadas con los operadores del álgebra SPARQL, en este sentido, FILTER aparece en un 92.31% y OPTIONAL en 82.35% lo que significa que son bastante comunes, debido al proceso de eliminación del servicio de idiomas utilizado y, en menor medida, UNION en un 6.79% y NOT EXISTS en un 5.43%, MINUS 1.81 % y EXISTS 0,45%. La cuarta parte tiene operadores de agregación, siendo el más utilizado GROUP BY en un 23.08%, el cual se combina con la mayoría de los operadores de agregación, dentro de los operadores, se destaca el uso de COUNT en un 19,91% y el resto se usa escasamente, en menos del 1,81%. Una vez mostrado el análisis básico sobre la presencia de palabras clave en las consultas, se analizará la estructura de los patrones triples. Esto es importante, porque nuestra noción de frescura se centra en los predicados. Esto restringe nuestro enfoque a patrones triples con una constante en la posición de predicado. Por lo tanto, nuestro primer objetivo es verificar la frecuencia de dichos patrones en las consultas. En la Tabla 1 (en el centro), se muestra que el patrón VCV (71.73%) es el más usado, VCC (18.05%) también es muy común y en total el 90.35% de los patrones triples tiene una constante en la posición de predicado, lo que ayuda a validar nuestra propuesta de usar un modelo basado en la dinámica de los predicados. Se evaluó la dinámica de los resultados de las consultas entre los resultados de la misma consulta en 23 versiones consecutivas de Wikidata. Se pudo identificar que aproximadamente la mitad de los resultados de las consultas cambian cada semana, como se ilustra en la Figura 2. En la Figura 2 se muestra para cada consulta, cuántos cambios se registraron durante 23 semanas. Existen consultas que cambian cada semana, otras que nunca cambiaron y una distribución bastante uniforme de los cambios en el período. En la Figura 3 se ilustra un análisis de la monotonicidad del resultado de la consulta en el período evaluado. Los puntos rojos significan solo resultados eliminados, los verdes agregados y los azules agregados y eliminados. Se puede observar que los resultados de la consulta no son monótonos.
Figura 2: A la izquierda, cambios en los resultados de las consultas por tiempo. A la izquierda, cambios en los resultados de las consultas por consulta.
import pandas as pd
import io
#load csv
#df = pd.read_csv('https://users.dcc.uchile.cl/~amoya/classdm1b.csv', sep=',')
#df = pd.read_csv('https://users.dcc.uchile.cl/~amoya/classdm1.csv', sep=',')
df = pd.read_csv('https://users.dcc.uchile.cl/~amoya/classdmb.csv', sep=',')
#df = pd.read_csv('https://users.dcc.uchile.cl/~amoya/classdm.csv', sep=',')
#df = pd.read_csv('https://users.dcc.uchile.cl/~amoya/class.csv', sep=',')
#df = pd.read_csv('https://users.dcc.uchile.cl/~amoya/ttl.csv', sep=',')
#En el dataset existen algunos atributos que no aportan a nuestros objetivos
#o representan redundancia con otros atributos.
#lista de atributos a analizar
features_to_plot = [e for e in list(df) if e not in
['query_id', 'dim_min', 'dim_max', 'changes_count']]
df[features_to_plot].describe()
A continuación mostramos la matriz de correlación entre los atributos de nuestro dataset. Los valores cercanos a 1 y -1 implican alta correlación, mientras que los valores cercanos a 0 indican una baja correlación
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns; sns.set(style="ticks", color_codes=True)
corr = df[features_to_plot].corr()
f, ax = plt.subplots(figsize=(10, 8))
sns.heatmap(corr, mask=np.zeros_like(corr, dtype=np.bool), cmap=sns.diverging_palette(220, 10, as_cmap=True), square=True, ax=ax)
Continuando con el análisis de la correlación entre los atributos y cuanto aportan a la correcta clasificación de nuestras nuestros datos, ploteamos su comportameinto por pares en relación a cada una de las clases de nuestro etiquetado.
#g = sns.pairplot(df, kind="reg") # Parametro kind="reg" agrega una recta
g = sns.pairplot(df[features_to_plot], hue="ttl", kind="reg") # Otra visualizacion
plt.show()
A continuación relizamos la normalización de los valores de nuestro dataset.
from sklearn import preprocessing
#remove query_id, dim_min, dim_max, changes_count and dim_class columns
features_names = [e for e in list(df) if e not in ['query_id', 'dim_min', 'dim_max', 'changes_count', 'past', 'dim_class', 'ttl']]
#y = df['dim_class']
#y = df['ttl']
#normalizing
X = df[features_names].values #returns a numpy array
min_max_scaler = preprocessing.MinMaxScaler()
X = min_max_scaler.fit_transform(X)
X
Uno de los datasets con los que probamos nuestros métodos, tiene en cuenta la dinamicidad de los predicados, lo que implica un dataset con un gran número de atributos. En cada fila se tendrían (además de los atributos relacionados a la sintaxis de la consulta) un 0 por cada atributo que no aparece en la consulta y para los que si aparecen, se tiene su dinamicidad. Por tanto, probamos el efecto de reducir la dimencionalidad de este dataset mediante la concervacion de la varianza (algoritmo PCA).
from sklearn.decomposition import PCA
#pca = PCA(n_components=50)
pca = PCA(.95)
X = pca.fit_transform(X)
X.shape
Para responder las preguntas de investigación realizamos cuatro experimentos. Los dos primeros para evaluar el desempeño de distintos clasificadores para predecir la dinamicidad de consultas teniendo en cuenta tres y dos clases de dinamicidad respectivamente, basadas en la frecuencia de cambio de los resultados.
El tercer experimento se realizó para determinar si es posible predecir el cambio en los resultados de las consultas en una semana usando clasificación. El último experimento fue intentar predecir el tiempo de vida de los resultados deuna consulta expresados en semanas y para lo cual utilizamos regreción lineal múltiple.
En nuestros experimentos se tuvieron en cuenta los clasificadores: Base Dummy, Decision Tree, Gaussian Naive Bayes, KNN y 3 tipos de SVM distintos (SVM-SVC, SVM-NuSVC y SVM-LinearSVC).
Se realizaron 100 corridas con cada clasificador dividiendo el dataset en entrenamiento (70%) y prueba (30%) y se muestran el desempeño promedio en prueba en terminos de las medidas f1-score, recall, precision y accuracy.
A continuación se muestran los resultados alcanzados:
Los resultados obtenidos en los experimentos muestran que de manera general la calidad obtenida en la clasificación por los distintos métodos no fue buena. Teniendo en cuenta esto, continuamos trabajando sobre la hipótesis inicial, pero consideramos que mejorar el etiquetado en la clasificación de clases de dinamicidad pueden mejorar los resultados alcanzados. Hasta el momento, para etiquetar las consultas según su dinamicidad solo se tuvo en cuenta la frecuencia de cambio de la consulta a lo largo de las 22 semanas. Una alternativa a esto, puede ser tener en cuenta nuevos rasgos obtenidos a partir de análisis estadísticos de comportamiento de cambio y su periodicidad; para intentar diferenciar las clases mediante algoritmos de agrupamiento.
Un primer paso hacia nuestros objetivos generales es identificar nodos con dinámicas similares. Por lo tanto, primero investigamos cómo agrupar consultas con respecto a características de su dinámica.
Las principales preguntas que debemos responder son: ¿Cuáles son las características que describen la dinámica de un recurso? Las características utilizadas fueron:
Frecuencia de cambio promedio: la dinámica de cada consulta se puede describir simplemente por el número de cambios de resultados de una consulta dividido por el número total de instantáneas en el período monitoreado.
Resúmenes estadísticos del comportamiento del cambio: podríamos utilizar resúmenes estadísticos de cambios en los resultados teniendo en cuenta la magnitud de tales cambios. Como tendencias centrales (media aritmética, mediana o media intercuartil) o dispersión (desviación estándar, varianza o cuantiles).
Periodicidades de los cambios: dichas periodicidades se determinaron mediante transformadas discretas de Fourier, se intentan solucionar los problemas obvios en el uso de frecuencia de cambio promedio (por ejemplo, una consulta cambia muy a menudo al comienzo del tiempo monitoreado pero luego es bastante estática, en comparación con otra que cambia regularmente en intervalos más grandes a lo largo del tiempo).
Para etiquetar los datos, tuvimos en cuenta dos ficheros, uno con el historial de cambios por consultas y otro con la magnitud de esos cambios.
import sys
import pandas as pd
data = pd.read_csv('https://users.dcc.uchile.cl/~amoya/cmp.csv', header=None, sep=',').T
data_count = pd.read_csv('https://users.dcc.uchile.cl/~amoya/count.csv', header=None, sep=',').T
data1=data.iloc[1:222,:]
data_count1=data_count.iloc[1:222,:]
data_fft=np.fft.fft(data1)
data_fft_real = data_fft.real
data_fft_real
Promedio de las longitudes de las subsecuencias estáticas y promedio de la frecuencia de cambios.
feats = np.zeros((221,24))
feats[:,:-2] = data_fft_real
c=0
avg_changes=[]
avg_zero_seqs=[]
for row in range(1,222):
#if not row in outliers:
sum=0
zero_seqs=0
in_seq = False
for col in range(22):
sum += data1[col][row]
if not data1[col][row] and not in_seq:
zero_seqs += 1
in_seq = True
elif data1[col][row]:
in_seq = False
avg_changes.append(sum/22)
avg_zero_seqs.append((22-sum)/zero_seqs if zero_seqs else 0)
feats[:,-2] = avg_changes
feats[:,-1] = avg_zero_seqs
c_avg = [data_count1.iloc[i,:].sum()/22 for i in range(0,219)]
c_std = [data_count1.iloc[i,:].std() for i in range(0,219)]
#feats[:,-2]=c_avg
#feats[:,-1]=c_std
import matplotlib.pyplot as plt
plt.plot(c_avg, 'ro-', linewidth=2)
# plt.axis([100, 150, -10, 400])
plt.show()
outliers = [126,208]
data1 = data_count1.drop(outliers)
data_count1 = data_count1.drop(outliers)
Aplicamos kmeans utilizando diferentes subconjuntos de características y evaluando la cohesión y separación de los clusters. A continuación mostramos el agrupamiento que obtuvo mejores índices de calidad.
import matplotlib.pyplot as plt
feats1=np.zeros((221,2))
feats1[:,:] = feats[:,-2:]
plt.plot(feats1[:,0], feats1[:,1], 'ro')
plt.show()
from sklearn.cluster import KMeans
from sklearn import metrics
from sklearn import preprocessing
#normalizing
min_max_scaler = preprocessing.MinMaxScaler()
feats_norm = min_max_scaler.fit_transform(feats1)
# Clustering with k=2..20
silhouette=[]
for k in range(2,21):
# Number of clusters
kmeans = KMeans(n_clusters=k)
# Fitting the input data
kmeans = kmeans.fit(feats1)
# Getting the cluster labels
labels = kmeans.predict(feats1)
# Centroid values
centroids = kmeans.cluster_centers_
# Save silhouette score
silhouette.append((k, metrics.silhouette_score(feats1, labels, metric='euclidean'), labels))
# Determining the maximum
best_k=0
best_metric=0
best_labels=[]
for i in range(len(silhouette)):
if silhouette[i][1] > best_metric:
best_k, best_metric, best_labels = silhouette[i]
y = best_labels
Para evaluar la calidad de los grupos encontrados utilizamos una medida de cohesión vista en clases WSS y otra que combina la cohesión y separación (Silhoette).
import matplotlib.pyplot as plt
plt.plot([m[0]for m in silhouette], [m[1]for m in silhouette], 'bo-', linewidth=2)
# plt.axis([0, 20, 0, 1])
plt.show()
Deducimos del análisis anterior que los mejores valores se obtienen con dos y tres grupos. Luego precedimos a utilizar estos grupos como etiquetas para la clasificación.
from sklearn.model_selection import cross_validate, cross_val_predict, train_test_split
from sklearn.metrics import f1_score, recall_score, precision_score, accuracy_score, classification_report
def run_classifier(clf, X, y, num_tests=100):
metrics = {'f1-score': [], 'precision': [], 'recall': []}
for _ in range(num_tests):
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.3, stratify=y)
clf.fit(X_train, y_train) ## Entrenamos con X_train y clases y_train
predictions = clf.predict(X_test) ## Predecimos con nuevos datos (los de test X_test)
metrics['f1-score'].append(f1_score(y_test, predictions, average='micro')) # X_test y y_test deben ser definidos previamente
metrics['recall'].append(recall_score(y_test, predictions, average='micro'))
metrics['precision'].append(precision_score(y_test, predictions, average='micro'))
return metrics
def run_cross_validation(clf, X, y, cv=7):
predictions = cross_val_predict(clf, X, y, cv=cv)
print("Metrics:")
print(classification_report(y, predictions))
print("f1_score:", f1_score(y, predictions, average='micro')) # X_test y y_test deben ser definidos previamente
print("recall_score:", recall_score(y, predictions, average='micro'))
print("precision_score:", precision_score(y, predictions, average='micro'))
print("Accuracy:", accuracy_score(y, predictions))
def run_linear_regression(clf, X, y):
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.3, stratify=y)
regr.fit(X_train, y_train)
y_pred = regr.predict(X_test)
print('Coefficients: \n', regr.coef_)
print("Mean squared error: %.2f" % math.sqrt(mean_squared_error(y_test, y_pred)))
print('Variance score: %.2f' % r2_score(y_test, y_pred))
from sklearn.dummy import DummyClassifier
from sklearn.svm import SVC, NuSVC, LinearSVC # support vector machine classifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.naive_bayes import GaussianNB # naive bayes
from sklearn.neighbors import KNeighborsClassifier
import numpy as np
c0 = ("Base Dummy", DummyClassifier(strategy='stratified'))
c1 = ("Decision Tree", DecisionTreeClassifier())
c2 = ("Gaussian Naive Bayes", GaussianNB())
c3 = ("KNN", KNeighborsClassifier(n_neighbors=25))
c4 = ("SVM-SVC", SVC(gamma=.001))
c5 = ("SVM-NuSVC", NuSVC(gamma=.001))
c6 = ("SVM-LinearSVC", LinearSVC(random_state=0, tol=1e-5))
SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0,
decision_function_shape='ovr', degree=3, gamma='scale', kernel='rbf',
max_iter=-1, probability=False, random_state=None, shrinking=True,
tol=0.001, verbose=False)
NuSVC(cache_size=200, class_weight=None, coef0=0.0,
decision_function_shape='ovr', degree=3, gamma='scale', kernel='rbf',
max_iter=-1, nu=0.5, probability=False, random_state=None,
shrinking=True, tol=0.001, verbose=False)
LinearSVC(C=1.0, class_weight=None, dual=True, fit_intercept=True,
intercept_scaling=1, loss='squared_hinge', max_iter=1000,
multi_class='ovr', penalty='l2', random_state=0, tol=1e-05, verbose=0)
classifiers = [c0, c1, c2, c3, c4, c6]
for name, clf in classifiers:
print("----------------")
print("Resultados para clasificador: ",name)
print("\nRandom partitions:")
metrics = run_classifier(clf, X, y)
print("Precision promedio:",np.array(metrics['precision']).mean())
print("Recall promedio:",np.array(metrics['recall']).mean())
print("F1-score promedio:",np.array(metrics['f1-score']).mean())
print("\nCross-validation:")
run_cross_validation(clf, X, y)
print("----------------\n\n")
A su vez, para predecir el tiempo que se mantendrán invariante los resultados de una consulta (ttl), decidimos modelarlo como la estimación de un valor continuo. Para esto entrenamos un modelo regresión lineal con la librería sklearn y tensorflow. Esta última nos permite ver cómo se va comportando la estimación por períodos (mediante el método de descenso del gradiente) y analizar la convergencia. Ambas arrojaron los mismos resultados.
from sklearn import linear_model
from sklearn.metrics import mean_squared_error, r2_score
# Create linear regression object
regr = linear_model.LinearRegression()
run_linear_regression(regr, X, y)
import math
from IPython import display
from matplotlib import cm
from matplotlib import gridspec
from matplotlib import pyplot as plt
from sklearn import metrics
import tensorflow as tf
from tensorflow.python.data import Dataset
tf.logging.set_verbosity(tf.logging.ERROR)
pd.options.display.max_rows = 10
pd.options.display.float_format = '{:.1f}'.format
def my_input_fn(features, targets, batch_size=1, shuffle=True, num_epochs=None):
# Convertir datos pandas en un dict de arrays np.
features = {key:np.array(value) for key,value in dict(features).items()}
# Construir un dataset, y configure batching/repeating.
ds = Dataset.from_tensor_slices((features,targets)) # warning: 2GB limit
ds = ds.batch(batch_size).repeat(num_epochs)
# Mezclar los datos, si se especifica.
if shuffle:
ds = ds.shuffle(buffer_size=10000)
# Devolver el nuevo lote de datos.
features, labels = ds.make_one_shot_iterator().get_next()
return features, labels
def train_model(X, y, learning_rate, steps, batch_size, features_names):
periods = 2
steps_per_period = steps / periods
#feature_data = california_housing_dataframe[[input_feature]]
#feature_target = "median_house_value"
#targets = california_housing_dataframe[feature_target]
# Crear columns característica.
feature_columns = [tf.feature_column.numeric_column(e) for e in features_names]
# Crear funciones input.
training_input_fn = lambda:my_input_fn(X, y, batch_size=batch_size)
prediction_input_fn = lambda:my_input_fn(X, y, num_epochs=1, shuffle=False)
# Crear un objeto linear regressor.
my_optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)
#my_optimizer = tf.contrib.estimator.clip_gradients_by_norm(my_optimizer, 5.0)
linear_regressor = tf.estimator.LinearRegressor(
feature_columns=feature_columns,
optimizer=my_optimizer
)
# Configuración para trazar el estado de la línea de nuestro modelo cada período.
#plt.figure(figsize=(15, 6))
#plt.subplot(1, 2, 1)
#plt.title("Línea aprendida por Período")
#plt.ylabel(feature_target)
#plt.xlabel(input_feature)
#sample = california_housing_dataframe.sample(n=300)
#plt.scatter(sample[input_feature], sample[feature_target])
#colors = [cm.coolwarm(x) for x in np.linspace(-1, 1, periods)]
# Entrena el modelo, pero haciéndolo dentro de un loop de modo que podamos periódicamente
# evaluar las métricas de pérdida.
print("Entrenamiento del modelo...")
print("RMSE (en datos de entrenamiento):")
root_mean_squared_errors = []
for period in range (0, periods):
# Entrenar el modelo, empezando desde el estado anterior.
linear_regressor.train(
input_fn=training_input_fn,
steps=steps_per_period
)
# Calcular predicciones.
predictions = linear_regressor.predict(input_fn=prediction_input_fn)
predictions = np.array([item['predictions'][0] for item in predictions])
# Calcular pérdida.
root_mean_squared_error = math.sqrt(metrics.mean_squared_error(predictions, y))
# Ocasionalmente imprimir la pérdida actual.
print(" período %02d : %0.2f" % (period, root_mean_squared_error))
# Agregar las métricas de pérdida de este período a nuestra lista.
root_mean_squared_errors.append(root_mean_squared_error)
# Por último, rastrea los pesos y los sesgos a lo largo del tiempo.
# Aplica algo de math para asegurarte que los datos y la línea se representan claramente.
#y_extents = np.array([0, sample[feature_target].max()])
#weight = linear_regressor.get_variable_value('linear/linear_model/%s/weights' % input_feature)[0]
#bias = linear_regressor.get_variable_value('linear/linear_model/bias_weights')
#x_extents = (y_extents - bias) / weight
#x_extents = np.maximum(np.minimum(x_extents, sample[input_feature].max()), sample[input_feature].min())
#y_extents = weight * x_extents + bias
#plt.plot(x_extents, y_extents, color=colors[period])
print("Entrenamiento del Modelo finalizado.")
# Muestra un gráfico de métricas de pérdida por períodos.
#plt.subplot(1, 2, 2)
#plt.ylabel('RMSE')
#plt.xlabel('Períodos')
#plt.title("Raíz Error Cuádratico Medio vs. Períodos")
#plt.tight_layout()
#plt.plot(root_mean_squared_errors)
# Muestra una tabla con los datos de calibración.
calibration_data = pd.DataFrame()
calibration_data['predictions'] = pd.Series(predictions)
calibration_data['targets'] = pd.Series(y['ttl'])
display.display(calibration_data.describe())
print("RMSE final(en datos de entrenamiento): %0.2f" % root_mean_squared_error)
return linear_regressor
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=.3, stratify=y)
X_train = pd.DataFrame(X_train, columns=[str(e) for e in range(1, 12)])
X_test = pd.DataFrame(X_test, columns=[str(e) for e in range(1, 12)])
y_train = pd.DataFrame(y_train)
y_test = pd.DataFrame(y_test)
linear_regressor = train_model(
X=X_train,
y=y_train,
learning_rate=0.00002,
steps=20000,
batch_size=50,
features_names=[str(e) for e in range(1, 12)]
)
print('Test del modelo...')
# Calcular predicciones.
predictions = linear_regressor.predict(input_fn=lambda:my_input_fn(X_test, y_test, num_epochs=1, shuffle=False))
predictions = np.array([item['predictions'][0] for item in predictions])
# Calcular pérdida.
root_mean_squared_error = math.sqrt(metrics.mean_squared_error(predictions, y_test))
# Ocasionalmente imprimir la pérdida actual.
print("RMSE (en datos de test): %0.2f" % (root_mean_squared_error))
Los resultados anteriores no son cloncluyentes, pero sugieren que es posible predecir los cambios futuros en los resultados de las consultas usando regresión lineal.
Los resultados obtenidos en los experimentos muestran que de manera general la calidad obtenida en la clasificación por los distintos métodos no fue buena, incluso utilizando las etiquetas devueltas por el agrupamiento. Teniendo en cuenta esto, no podemos demostrar el cumplimiento de la hipótesis inicial, pero consideramos que pueden mejorar los resultados alcanzados si tomamos una muestra de consultas más representativa y aumentar la ventana de tiempo de monitoreo de las mismas y del dataset.
Para ampliar el dataset de consultas Actualmente nuestro dataset de consultas no es representativo, cuenta con solo 221 consultas que solo usan 153 predicados de los 3360 posibles. En este sentido, estamos trabajando en replicar todo este análisis sobre el dataset Wikidata SPARQL Logs y generar nuevas consultas automáticamente en lenguaje SPARQL.
Tambien consideraremos agregar características de la semántica de los operadores en las consultas y otras características de la dinamicidad de los datos.
Es válido señalar que se realizaron cambios teniendo en cuenta las observaciones realizadas en el hito 2, tales como: concretar la hipótesis general y las preguntas de investigación a contestar; se realizaron experimentos para determinar si las consultas que se tenían eran representativas.
[Cho and Garcia-Molina, 2000] Junghoo Cho and Hector Garcia-Molina. The evolution of the web and implications for an incremental crawler. In VLDB, pages 200–209, 2000.
[Haslhofer y Popitsch, 2009] B. Haslhofer and N. Popitsch. DSNnotify - detecting and fixing broken links in linked data sets. In DEXA ’09 Workshop on Web Semantics (WebS ’09), 2009.
[Pandey et al., 2003] Sandeep Pandey, Krithi Ramamritham, and Soumen Chakrabarti. Monitoring the dynamic web to respond to continuous queries. In World Wide Web Conference (WWW ’03), pages 659–668, 2003.